////////////////////////////////////////////////////////////////////////////////
//
// Aranya Code
// author: TonyHack
// E-mail: tonyjobmails@gmail.com
//
// filename: encoding.cpp
// function:  字符编码相关函数定义
//
////////////////////////////////////////////////////////////////////////////////

#include "encoding.h"
#include <strings.h>

namespace aranya_encoding
{
	///////////////////////////////////////////////////////////
	/// @brief 编码转换(对参数中的字符串不保证完全安全，请保证输入参数完全正确)
	/// @param from：输入的编码格式
	/// @param to：输出的编码格式
	/// @param p_text：输入字符串
	/// @param len_t：输入字符串的长度
	/// @param len_r：输出字符串的长度
	/// @return 转换后的字符串
	char * encode_convert(const char *from, const char *to, char *p_text, int len_t, int *len_r)
	{
		if(from && to && p_text && len_t)
		{
			iconv_t iv = 0;
			iv = iconv_open(from, to);
			if(iv != (iconv_t) -1)
			{
				size_t len_max = len_t * 2 + 1;
				size_t len_in = len_t;
				char *result = (char *)malloc(len_max);
				*len_r = (int)len_max;
				if(result)
				{
					///////////////////////////////////////////////
					/// 这个地方给出个说明，关于iconv用法
					/// 原型 size_t iconv (iconv_t cd, const char* * inbuf, size_t * inbytesleft, char* * outbuf, size_t * outbytesleft);
					/// 返回值 size_t 返回本次调用中转换的字符数，可逆的转换不计入。出错时，它将修改errno并返回(size_t)(-1)。
					/// cd：iconv_open函数创建的转换描述符
					/// inbuf：输入的字符串
					/// inbytesleft：输入转换的字符数，转换后等于剩余的字符数
					/// outbuf：输出的字符串
					/// outbytesleft：输出转换字符数，转换后等于输出剩余的字符数
					/// 不理解这两个left是干嘛的
					size_t ret = iconv(iv, &p_text, &len_in, &result, &len_max);
					///////////////////////////////////////////////
					if (ret != (size_t)-1)
					{
						iconv_close(iv);
						return result;
					}
				}
			}
			iconv_close(iv);
		}
		return NULL;
	}
	///////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////
	/// @brief UTF-8转GB2312(C语言版)(对参数中的字符串不保证完全安全，请保证输入参数完全正确)
	/// @param p_text：待转换的输入字符串
	/// @param len_r：输入字符串长度
	/// @param p_result：转换后的字符串
	/// @param len_t：转换后的字符串长度
	/// @return 成功与否
	bool urt8_to_gb2312(char *p_text, int len_t, char *p_result, int &len_r)
	{
		return true;
	}
	///////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////
	/// @brief UTF-8转GB2312(C++版)
	/// @param s：字符串，是输入，也是输出
	/// @return 成功与否
	bool urt8_to_gb2312(std::string &s)
	{
		return true;
	}
	///////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////
	/// @brief GB2312转UTF-8(C语言版)(对参数中的字符串不保证完全安全，请保证输入参数完全正确)
	/// @param p_text：待转换的输入字符串
	/// @param len_r：输入字符串长度
	/// @param p_result：转换后的字符串
	/// @param len_t：转换后的字符串长度
	/// @return 成功与否
	bool gb2312_to_utf8(char *p_text, int len_t, char *p_result, int &len_r)
	{
		return true;
	}
	///////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////
	/// @brief GB2312转UTF-8(C++版)
	/// @param s：字符串，是输入，也是输出
	/// @return 成功与否
	bool gb2312_to_utf8(std::string &s)
	{
		return true;
	}
	///////////////////////////////////////////////////////////
};

namespace aranya_url_encoding
{
	/// 十六进制对应的字符集
	static unsigned char hex_char[] = "0123456789ABCDEF";

	///////////////////////////////////////////////////////////
	/// @brief 是否是非安全字符(不用编码)
	/// @param data：待检查的字符
	/// @return 是否是非安全字符
	bool is_unsave_char(const char data)
	{
		/// 不是数字，英文字母，不是'-' '_'，其它字符看作不安全字符
		return ((data < '0' && data != '-') || (data < 'A' && data > '9') || (data > 'Z' && data < 'a' && data != '_') || (data > 'z'));
	}
	///////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////
	/// @brief URL编码(C语言版)，返回编码后的结果
	/// @param p_text：原url字符串
	/// @param len：原url长度
	/// @param encode_len：编码后的url长度
	/// @return 编码后的字符串
	char* url_encode(const char *p_text, int len, int *encode_len)
	{
		if(p_text && len)
		{
			register int pos = 0;
			register int idx = 0;
			char *text;

			/// 转化的最大值为3 * len + 1，也就是说每一个字符都要编码成%XX，最后加一个结束符
			text = new char[3 * len + 1];
			if(text)
			{
				bzero(text, sizeof(text));

				while(len--)
				{
					/// 空格转化成'+'
					if(p_text[pos] == ' ')
					{
						text[idx] = p_text[pos];
					}
					/// 不安全字符进行编码
					else if(is_unsave_char(p_text[pos]))
					{
						/// 转化为一个%XX
						text[idx++] = '%';
						/// 取高四位
						text[idx++] = hex_char[(unsigned char)p_text[pos] >> 4];
						/// 取低四位
						text[idx] = hex_char[(unsigned char)p_text[pos] & 15];
					}
					/// 其它字符不变
					else
					{
						text[idx] = p_text[pos];
					}
					++pos;
					++idx;
				}
				text[idx] = 0;
				if(encode_len)
				{
					*encode_len = idx;
				}
				return text;
			}
		}
		return NULL;
	}
	///////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////
	/// @brief URL编码(C++语言版)
	/// @param s：输入和输出的字符串
	bool url_encode(std::string &s)
	{
		char *tmp = url_encode(s.c_str(), s.length(), NULL);
		if(tmp)
		{
			s = tmp;
			free(tmp);
			return true;
		}
		return false;
	}
	///////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////
	/// @brief URL解码(C语言版)，参数p_text做为输入也做为输出
	/// @param p_text：原url字符串
	/// @param len：原url长度
	/// @param encode_len：解码后的url长度
	/// @return 解码是否成功
	bool url_decode(char *p_text, int len, int *encode_len)
	{
		if(p_text && len)
		{
			register int pos = 0;
			register int idx = 0;
			while(p_text[pos] && p_text[idx])
			{
				/// ‘+’ 转化为' '
				if(p_text[pos] == '+')
				{
					p_text[idx] = ' ';
				}
				/// %XX转化
				else if(p_text[pos] == '%' && pos < len-2 && isxdigit(p_text[pos+1]) && isxdigit(p_text[pos+2]))
				{
					int tmp_h = tolower(hexc_to_i((int)p_text[pos+1]));
					int tmp_l = tolower(hexc_to_i((int)p_text[pos+2]));
					p_text[idx] = 16 * tmp_h + tmp_l;
					pos +=2;
				}
				else return false;
				++pos;
				++idx;
			}
			p_text[idx] = 0;
			*encode_len = idx;
			return true;
		}
		return false;
	}
	///////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////
	/// URL 解码(C++语言版)
	bool url_decode(std::string &s)
	{
		int len = 0;
		char tmp[s.length() + 1];
		strncpy(tmp, s.c_str(), sizeof(tmp));
		if(url_decode(tmp, s.length(), &len))
		{
			s = tmp;
			return true;
		}
		return false;
	}
	///////////////////////////////////////////////////////////
};
